package com.iceland.client.client.cmd;

import com.iceland.client.client.Client;
import com.iceland.client.client.cmd.Strategy.ForwardStrategy;
import com.iceland.client.client.proxy.ProxyClient;
import com.iceland.client.config.Environment;
import com.iceland.client.config.ProxyConfig;
import com.iceland.client.util.ClientChannelContext;
import com.iceland.common.exception.GlobalExceptionHandler;
import com.iceland.common.message.CmdMessagEncoder;
import com.iceland.common.message.CmdMessageDecoder;
import com.iceland.common.message.MessageFactory;
import com.iceland.common.rpc.*;
import com.iceland.common.strategy.CmdStrategyFactory;
import com.iceland.common.thread.MessageEventExcutor;
import com.iceland.common.util.AttributeKeyUtils;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

@Slf4j
public class CmdClient extends Client {

    static {
        ProxyConfig proxyConfig = Environment.getProxyConfig();
        //设置线程数
        MessageEventExcutor.setCorePoolSize(proxyConfig.getCoreMessageEventThreadSize());
        MessageEventExcutor.setMaxPoolSize(proxyConfig.getMaxMessageEventThreadSize());
        //扫描服务接口
        RemoteServiceScanner.scan(null);
        log.info("扫描服务接口:{}",RemoteServiceScanner.getInterfaceClass());
        log.info("扫描服务实例:{}",RemoteServiceScanner.getInstanceClass());

        List<Class> instanceClass = RemoteServiceScanner.getInstanceClass();
        instanceClass.forEach(x->{
            RpcServiceClassFactory.putBean(x);
        });
        log.info("载入服务实例:{}",RemoteServiceScanner.getInstanceClass());
        log.info("初始化CmdStrategyFactory，载入策略MethodInvocationStrategy、ForwardStrategy、MethodInvocationResponseStrategy");
        CmdStrategyFactory.addCmdStrategy(new ForwardStrategy());
        CmdStrategyFactory.addCmdStrategy(new MethodInvocationStrategy());
        CmdStrategyFactory.addCmdStrategy(new MethodInvocationResponseStrategy());
    }

    RpcMessageManager rpcMessageManager;

    public ProxyClient getProxyClient() {
        return proxyClient;
    }

    public void setProxyClient(ProxyClient proxyClient) {
        this.proxyClient = proxyClient;
    }

    String cmdServerAddress;
    ProxyClient proxyClient;
    public CmdClient(ProxyClient proxyClient) {
        super(2, NioSocketChannel.class);
        this.proxyClient=proxyClient;
    }

    @Override
    protected void preStart(Bootstrap bs) {

        bs.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
                //out:逆序执行
                ch.pipeline().addFirst( new CmdMessagEncoder());
                ch.pipeline().addLast(new GlobalExceptionHandler());

                //in：顺序执行
                ch.pipeline().addLast(new CmdMessageDecoder(),new CmdHandler());
            }
        });
    }

    @Override
    protected void afterStart() {

    }

    /**
     * 建立命令通道
     */
    public ChannelFuture connectCmdServer(String cmdServerAddress){
        this.cmdServerAddress=cmdServerAddress;
        if(rpcMessageManager!=null){
            log.warn("此动作将关闭之前的通道，并建立新的通道");
        }

        log.info("开始连接命令服务器{}",cmdServerAddress);
        String[] cmdServerAddressSplit = cmdServerAddress.split(":");
        ChannelFuture connect = bs.connect(cmdServerAddressSplit[0], Integer.parseInt(cmdServerAddressSplit[1]));

        //通道初始化上下文
        Channel channel=connect.channel();
        if(rpcMessageManager==null){
            rpcMessageManager=new RpcMessageManager();
        }else {
            //关闭原来的通道
            if(rpcMessageManager.getChannel()!=null){
                rpcMessageManager.getChannel().close();
            }
        }
        //更换为新的通道
        rpcMessageManager.setChannel(channel);
        ClientChannelContext channelContext=new ClientChannelContext(rpcMessageManager,null,this,proxyClient);
        AttributeKeyUtils.setChannelContext(channel,channelContext);
        //发送命令
        try {
            connect.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if(future.isSuccess()){
                        //告知建立命令通道消息
                        future.channel().writeAndFlush(MessageFactory.cmdChannelConnectMessage(cmdServerAddress));
                        log.info("发送建立命令通道消息");
                    }
                }
            }).sync();

        } catch (InterruptedException e) {
            log.debug("连接失败",e);
            log.error("连接命令服务器失败{}",cmdServerAddress);
        }

        log.info("连接命令服务器成功{}",cmdServerAddress);
        return connect;
    }


    /**
     * 建立数据交换通道
     */
//    public ChannelFuture connectDataExchangeServer(String cmdServerAddress,String proxyServerAddress){
//        String[] cmdServerAddressSplit = cmdServerAddress.split(":");
//        ChannelFuture connect = bs.connect(cmdServerAddressSplit[0], Integer.parseInt(cmdServerAddressSplit[1]));
//        connect.addListener(new ChannelFutureListener() {
//            @Override
//            public void operationComplete(ChannelFuture future) throws Exception {
//                if(future.isSuccess()){
//                    //告知建立数据交换通道
//                    future.channel().writeAndFlush(MessageFactory.dataExchangeChannelConnectMessage(proxyServerAddress));
//
//                }
//            }
//        });
//        return connect;
//    }

    /**
     * 建立数据交换通道
     */
    public ChannelFuture connectDataExchangeServer(Integer proxyServerPort){
        String[] cmdServerAddressSplit = cmdServerAddress.split(":");
        ChannelFuture connect = bs.connect(cmdServerAddressSplit[0], Integer.parseInt(cmdServerAddressSplit[1]));
        connect.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if(future.isSuccess()){
                    //告知建立数据交换通道
                    future.channel().writeAndFlush(MessageFactory.dataExchangeChannelConnectMessage(cmdServerAddressSplit[0]+":"+String.valueOf(proxyServerPort)));
                }
                else{
                    log.error("告知建立数据交换通道失败");
                }
            }
        });
        return connect;
    }

    public RpcMessageManager getRpcMessageManager() {
        return rpcMessageManager;
    }
}
